namespace LightBox
{
    using System;
    using System.ComponentModel;
    using System.Globalization;
    using System.Security.Permissions;
    using System.Web;
    using System.Web.UI;
    using System.Web.UI.WebControls;
    using System.Web.UI.Design;
    using System.Web.UI.HtmlControls;

    public class HyperLinkTemplate : LightBox.HyperLink, IDataItemContainer
    {
        public HyperLinkTemplate(object dataItem, int index)
        {
            _DataItem = dataItem;
            _DataItemIndex = _DisplayIndex = index;
        }

        private object _DataItem;
        public object DataItem
        {
            get { return _DataItem; }
        }

        private int _DataItemIndex;
        public int DataItemIndex
        {
            get { return 0; }
        }

        private int _DisplayIndex;
        public int DisplayIndex
        {
            get { return 0; }
        }
    }

    /// <summary>
    /// A lot of the info for the Gallery control was gleaned from Jeffrey Palermo @ http://weblogs.asp.net/palermo4/archive/2007/01/10/creating-a-custom-databound-templated-control-in-asp-net-2-0.aspx 
    /// </summary>
    [ToolboxData("<{0}:gallery runat='server'></{0}:gallery>")]
    [AspNetHostingPermission(SecurityAction.LinkDemand, Level = AspNetHostingPermissionLevel.Minimal)]
    [AspNetHostingPermission(SecurityAction.InheritanceDemand, Level = AspNetHostingPermissionLevel.Minimal)]
    [PersistChildren(true)]
    public class Gallery : CompositeDataBoundControl
    {
        // TODO: Option to parse children of the gallery rather than enumerate directory.
        // TODO: Figure out how to get rid of the containing span and replace with a div. Possibly use TemplateControl.
        // TODO: Figure out how to handle deleting of source images and still use resized.

        //protected   override  void Render(HtmlTextWriter writer)
        //{
        //    writer.RenderBeginTag(HtmlTextWriterTag.Div);
        //    CreateChildControls();
        //    writer.RenderEndTag();

        //}

        //private ITemplate _HeaderTemplate;
        //[PersistenceMode(PersistenceMode.InnerProperty),
        //TemplateContainer(typeof(HyperLinkTemplate))]
        //public ITemplate HeaderTemplate
        //{
        //    get { return _HeaderTemplate; }
        //    set { _HeaderTemplate = value; }
        //}

        //private ITemplate _FooterTemplate;
        //[PersistenceMode(PersistenceMode.InnerProperty),
        //TemplateContainer(typeof(HyperLinkTemplate))]
        //public ITemplate FooterTemplate
        //{
        //    get { return _FooterTemplate; }
        //    set { _FooterTemplate = value; }
        //}

        #region Properties

        /// <summary>
        /// If blank the original image is overwritten and DeleteSource is ignored.
        /// </summary>
        [Bindable(true), Localizable(true)]
        [Category("Appearance"), DefaultValue("_Large")]
        public virtual string LargeDirectory
        {
            get { return ViewState["LargeDirectory"] as string; }
            set { ViewState["LargeDirectory"] = value; }
        }

        /// <summary>
        /// 
        /// </summary>
        [Bindable(true), Localizable(true)]
        [Category("Appearance"), DefaultValue("Small")]
        public virtual string SmallDirectory
        {
            get { return ViewState["SmallDirectory"] as string; }
            set { ViewState["SmallDirectory"] = value; }
        }

        /// <summary>
        /// 
        /// </summary>
        [Bindable(true), Localizable(true)]
        [Category("Appearance"), DefaultValue("400,300")]
        public virtual System.Drawing.Size LargeSize
        {
            get { return null == ViewState["LargeSize"] ? new System.Drawing.Size() : (System.Drawing.Size)ViewState["LargeSize"]; }
            set { ViewState["LargeSize"] = value; }
        }

        /// <summary>
        /// 
        /// </summary>
        [Bindable(true), Localizable(true)]
        [Category("Appearance"), DefaultValue("200,150")] // TODO: Figure out these defaults.
        public virtual System.Drawing.Size SmallSize
        {
            get { return null == ViewState["SmallSize"] ? new System.Drawing.Size() : (System.Drawing.Size)ViewState["SmallSize"]; }
            set { ViewState["SmallSize"] = value; }
        }

        /// <summary>
        /// 
        /// </summary>
        [Bindable(true), Localizable(true)]
        [Category("Appearance"), DefaultValue(false)]
        public virtual bool DeleteSource
        {
            get { return (bool)ViewState["DeleteSource"]; }
            set { ViewState["DeleteSource"] = value; }
        }

        /// <summary>
        /// 
        /// </summary>
        [Bindable(true), Localizable(true)]
        [Category("Appearance"), DefaultValue(false)]
        public virtual string Group
        {
            get { return ViewState["Group"] as string; }
            set { ViewState["Group"] = value; }
        }
        #endregion

        /// <summary>
        /// The reverse of Server.MapPath.
        /// Converted to C# after being purloined from http://geekswithblogs.net/alslog/archive/2006/08/03/87032.aspx
        /// </summary>
        /// <param name="Path"></param>
        /// <returns></returns>
        public string MapUrl(string Path)
        {
            string AppPath = HttpContext.Current.Server.MapPath("~");
            string url = String.Format("~/{0}", Path.Replace(AppPath, "").Replace("\\", "/"));
            return url;
        }

        // TODO: Handle relative and file system directories. ~ and C:\xxx

        protected override int CreateChildControls(System.Collections.IEnumerable dataSource, bool dataBinding)
        {
            int count = 0;
            if (dataBinding)
            {
                // TODO: Make this part of a header/footer template.
                this.Controls.Add(new System.Web.UI.LiteralControl("<div class=\"lightbox\">"));

                //        // TODO: Place diagnostic permissions check in here for the resizing, use CAS as well as helpful exception.
                //// TODO: Create the two directories if needed.
                foreach (object dataItem in dataSource)
                {
                    {
                        LightBox.HyperLinkTemplate hl = new LightBox.HyperLinkTemplate(dataItem, count++);
                        System.IO.FileInfo fi = dataItem as System.IO.FileInfo;

                        if (System.IO.File.Exists(fi.Directory.ToString() + "\\" + LargeDirectory + "\\" + fi.Name) == false)
                        {
                            // Large file does not exist, create it.
                            ResizeImage(fi.FullName, fi.Directory.ToString() + "\\" + LargeDirectory + "\\" + fi.Name, System.Drawing.Imaging.ImageFormat.Jpeg, LargeSize, true);
                        }
                        if (System.IO.File.Exists(fi.Directory.ToString() + "\\" + SmallDirectory + "\\" + fi.Name) == false)
                        {
                            // Small file does not exist, create it.
                            ResizeImage(fi.FullName, fi.Directory.ToString() + "\\" + SmallDirectory + "\\" + fi.Name, System.Drawing.Imaging.ImageFormat.Jpeg, SmallSize, true);
                            ExifWorks.ExifManager.CopyExifData(fi.FullName, fi.Directory.ToString() + "\\" + SmallDirectory + "\\" + fi.Name);
                        }

                        ExifWorks.ExifManager Exif = new ExifWorks.ExifManager(fi.FullName);

                        hl.ImageUrl = MapUrl(fi.Directory.ToString()) + "/" + SmallDirectory + "/" + fi.Name;
                        hl.NavigateUrl = MapUrl(fi.Directory.ToString()) + "/" + LargeDirectory + "/" + fi.Name;
                        hl.ToolTip = Exif.UserComment;
                        hl.Group = Group;

                        //DEBUG this.Controls.Add(new System.Web.UI.LiteralControl(Exif.Description));

                        // Put our hyperlink template into the template space.
                        //ItemTemplate.InstantiateIn(hl);
                        Exif.Dispose();

                        // add item to Controls collection
                        this.Controls.Add(hl);

                        // need to support <%# %> expressions
                        hl.DataBind();
                    }
                    //// add a break between each item
                    //this.Controls.Add(new LiteralControl("<br />"));
                }

                this.Controls.Add(new System.Web.UI.LiteralControl("</div>"));
            }
            return count;
        }

        /// <summary>
        /// Resize an image with high quality sampling.
        /// </summary>
        /// <param name="SourcePath"></param>
        /// <param name="DestPath"></param>
        /// <param name="Format"></param>
        /// <param name="WidthAndHeight"></param>
        /// <param name="MaintainAspect">If true then keep the image aspect ratio the same as the source to avoid squashed looking images.</param>
        /// <remarks>TODO: Need to copy the source Exif data as it does not survive a resize.
        /// This code is really only intentended to be used to reduce image sizes, not to expand them as you'll get loss of quality that way.
        /// </remarks>
        public void ResizeImage(string SourcePath, string DestPath, System.Drawing.Imaging.ImageFormat Format, System.Drawing.Size NewSize, bool MaintainAspect)
        {
            System.Drawing.Image SourceImage = System.Drawing.Image.FromFile(SourcePath);

            // Only resize if the image size required is greater than the source image in width or height.
            if (NewSize.Width < SourceImage.Width && NewSize.Height < SourceImage.Height)
            {
                if (true == MaintainAspect && (NewSize.Width < SourceImage.Width && NewSize.Height < SourceImage.Height))
                {
                    float Ratio;
                    if (((float)NewSize.Width / (float)SourceImage.Width) < ((float)NewSize.Height / (float)SourceImage.Height))
                    {
                        Ratio = (float)NewSize.Width / (float)SourceImage.Width;
                    }
                    else
                    {
                        Ratio = (float)NewSize.Height / (float)SourceImage.Height;
                    }
                    NewSize.Height = (int)((float)SourceImage.Height * Ratio);
                    NewSize.Width = (int)((float)SourceImage.Width * Ratio);
                    // TODO: When sized proportionally, add option to use transparent background or other colour to pad to original resize spec size.
                }
                System.Drawing.Image ImgResized = new System.Drawing.Bitmap(NewSize.Width, NewSize.Height, SourceImage.PixelFormat);

                System.Drawing.Graphics g = System.Drawing.Graphics.FromImage(ImgResized);

                g.CompositingQuality = System.Drawing.Drawing2D.CompositingQuality.HighQuality;
                g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;
                g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic;

                System.Drawing.Rectangle Rect = new System.Drawing.Rectangle(0, 0, NewSize.Width, NewSize.Height);
                g.DrawImage(SourceImage, Rect);
                ImgResized.Save(DestPath, Format);
            }
            else
            {
                // Just copy the source to the output path as it is already smaller than we want (we don't upsize because it loses quality).
                SourceImage.Save(DestPath, Format);
            }

            SourceImage.Dispose();
        }
    }

    //public class GalleryDesigner : ControlDesigner
    //{
    //    public override void Initialize(IComponent Component)
    //    {
    //        base.Initialize(Component);
    //        SetViewFlags(ViewFlags.TemplateEditing, true);
    //    }

    //    public override string GetDesignTimeHtml()
    //    {
    //        return "<span>This is design-time HTML</span>";
    //    }

    //    public override TemplateGroupCollection TemplateGroups
    //    {
    //        get
    //        {
    //            TemplateGroupCollection collection = new TemplateGroupCollection();
    //            TemplateGroup group;
    //            TemplateDefinition template;
    //            Gallery control;

    //            control = (Gallery)Component;
    //            group = new TemplateGroup("Item");
    //            template = new TemplateDefinition(this, "Template", control, "Template", true);
    //            group.AddTemplateDefinition(template);
    //            collection.Add(group);
    //            return collection;
    //        }
    //    }
    //}
}
